﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Windows.Threading;

namespace ManPowerEngine
{
	/// <summary>
	/// time argument passed to events
	/// </summary>
	public class TimeArgs
	{
		/// <summary>
		/// Seconds passed since last frame
		/// </summary>
		public double SinceLastFrame;
		/// <summary>
		/// Seconds passed since game started
		/// </summary>
		public double SinceStarted;
		/// <summary>
		/// How many frame per second
		/// </summary>
		public int FPS;
		/// <summary>
		/// Get the current time statistics
		/// </summary>
		public static TimeArgs Now
		{
			get
			{
				return msNow;
			}
		}
		internal static TimeArgs msNow;
	}
	/// <summary>
	/// The MPE Application base class, this implements the main game loop
	/// </summary>
	public abstract class ApplicationBase
	{
		/// <summary>
		/// Implement this for creating facilities of this application
		/// </summary>
		protected abstract void Create();

		/// <summary>
		/// Implement this for events before update anything
		/// </summary>
		/// <param name="timespan">Time elapsed since last frame</param>
		protected virtual void Update(TimeArgs Time) { }

		/// <summary>
		/// Implement this for events after update anything and before physics simulation
		/// </summary>
		protected virtual void PostUpdate(TimeArgs Time) { }

		/// <summary>
		/// Implement this for events after physics simulation and before rendering
		/// </summary>
		protected virtual void PreRender(TimeArgs Time) { }

		// static links to objects
		private static ApplicationBase msInstance = null;
		private DispatcherTimer dispatcherTimer;

		private DateTime mStarted;
		private DateTime mLastTick;

		private int mFPS = 0;
		private int mFrameCounter = 0;
		private double mLastSecond = 0;
		

        public static ApplicationBase Instance
        {
            get
            {
                return msInstance;
            }
        }

		/// <summary>
		/// Create this Application class
		/// </summary>
        public ApplicationBase()
        {
			msInstance = this;
        }

        private void _Update(object sender, EventArgs ea)
        {
			// prepare time arguments
            DateTime now = DateTime.Now;
            TimeSpan elapsedlasttick = now - mLastTick;
			TimeSpan elapsedstarted = now - mStarted;
            mLastTick = now;
			TimeArgs TimeElapsed = new TimeArgs();
			TimeElapsed.SinceLastFrame = elapsedlasttick.TotalSeconds;
			TimeElapsed.SinceStarted = elapsedstarted.TotalSeconds;
			if (TimeElapsed.SinceStarted - mLastSecond >= 1)
			{
				mLastSecond = TimeElapsed.SinceStarted;
				mFPS = mFrameCounter;
				mFrameCounter = 0;
			}
			mFrameCounter++;
			TimeElapsed.FPS = mFPS;
			TimeArgs.msNow = TimeElapsed;

			// Update routine
			Update(TimeElapsed);
			GameObject.Root._Update(TimeElapsed);
			GameObject.OverlayRoot._Update(TimeElapsed);
			PostUpdate(TimeElapsed);

			// physics engine integration
			PhysicsSystem.Instance.SimulatePhysics(TimeElapsed);

			// call custom pre-render event
			PreRender(TimeElapsed);
			
			// render this frame
			RenderSystem.Instance.RenderFrame();
        }

		/// <summary>
		/// Start running this MPE Application
		/// </summary>
		/// <param name="target">The target framework element, e.g. MainPage</param>
		/// <param name="canvas">The rendering target canvas, e.g. LayoutRoot</param>
		/// <param name="width">Width of the screen, e.g. 800</param>
		/// <param name="height">Height of the screen, e.g. 600</param>
		/// <param name="assemblyname">the assembly name of current project</param>
		/// <param name="fps">Your preferred FPS for this application</param>
		/// <param name="physicsiteration">how many iterations of physics simulation will be performed for each frame</param>
		public void Run(FrameworkElement target, Canvas canvas, int width, int height, string assemblyname, double fps, int physicsiteration)
        {
			// create the root and overlay root game object
			new GameObject("Root");
			new GameObject("OverlayRoot");

			// create the render system
			new RenderSystem(canvas, assemblyname, width, height);

			// create the render system
			new PhysicsSystem(physicsiteration);

			// create the render system
			new InputSystem(target);
			new SoundSystem();

			// call Create() to do custom initialization
			Create();

			// let the timer start running
			dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
			dispatcherTimer.Tick += new EventHandler(_Update);
			dispatcherTimer.Interval = TimeSpan.FromMilliseconds(1000 / fps);
			mStarted = DateTime.Now;
			mLastTick = mStarted;

			// start the timer
			dispatcherTimer.Start();
        }

		/// <summary>
		/// Start running this MPE Application
		/// </summary>
		/// <param name="target">The target framework element, e.g. MainPage</param>
		/// <param name="canvas">The rendering target canvas, e.g. LayoutRoot</param>
		/// <param name="width">Width of the screen, e.g. 800</param>
		/// <param name="height">Height of the screen, e.g. 600</param>
		/// <param name="assemblyname">the assembly name of current project</param>
		/// <param name="fps">Your preferred FPS for this application</param>
		public void Run(FrameworkElement target, Canvas canvas, int width, int height, string assemblyname, double fps)
		{
			Run(target, canvas, width, height, assemblyname, fps, 5);
		}

		/// <summary>
		/// Start running this MPE Application
		/// </summary>
		/// <param name="target">The target framework element, e.g. MainPage</param>
		/// <param name="canvas">The rendering target canvas, e.g. LayoutRoot</param>
		/// <param name="width">Width of the screen, e.g. 800</param>
		/// <param name="height">Height of the screen, e.g. 600</param>
		/// <param name="assemblyname">the assembly name of current project</param>
		public void Run(FrameworkElement target, Canvas canvas, int width, int height, string assemblyname)
		{
			Run(target, canvas, width, height, assemblyname, 20);
		}

		/// <summary>
		/// Stop Application and unload everything
		/// </summary>
		public void Stop()
		{
			dispatcherTimer.Stop();
			dispatcherTimer.Tick -= new EventHandler(_Update);
			InputSystem.Instance.CleanUp();
			RenderSystem.Instance.CleanUp();
			GameObject.Root.CleanUp();
		}
    }
}


